<?
/**
 * The file contains vc_data_driver class responsible for compressing/decompressing data
 * 
 * @package	VC
 * @author	cryonyx
 */

//	We need logger class plugged in so that logger functionality works fine 
require_once(dirname(__FILE__) . "/vc.logger.class.php");

/**
 * Class is responsible for compressing/decompressing data in format of Views Counter
 */
class vc_data_driver {
	
	const	VERSION = '0.1';
	
	/**
	 * Logger object (if exists)
	 *
	 * @var vc_logger
	 */
	private	$logger;
	
	public function __construct() {
		if(class_exists("vc_logger")) {
			$this->logger	=	new vc_logger();
		} else {
			$this->logger	=	NULL;
		}
	}
	
	/**
	 * Compresses statistics for one entry
	 * 
	 * @param	integer	$views
	 * @param	array	$visitors
	 * 
	 * @return	string
	 */
	public function compress($views, Array $visitors) {
		//	Make sure that views number is a number
		$views	=	intval($views);
		//	Transform views number to HEX representation
		$retval	=	sprintf("%X",$views) . '/';
		
		self::logThis('Starting transform. Views number: ' . $views, 0, 'vc_data_driver::compress');
		
		foreach($visitors as $ip) {
			$sections	=	explode('.', $ip);
			//	If format is not of IPv4, then just ignoring it
			if(count($sections) != 4) {
				self::logThis('Wrong IP format. Expected IPv4, got: ' . $ip, 3, 'vc_data_driver::compress');
				continue;
			}
			
			foreach($sections as $section) {
				//	Convert to 2-digit HEX and add to result string
				$retval	.=	sprintf("%02X", intval($section));
			}
		}
		
		self::logThis('Result string: ' . $retval, 0, 'vc_data_driver::compress');
		
		return $retval;
	}
	
	/**
	 * Decompresses statistics for one entry
	 * 
	 * @param	string	$dataString
	 * 
	 * @return	array
	 */
	public function decompress($dataString) {
		//	If argument is incorrect, return NULL
		if(	!isset($dataString)	||
			("" == trim($dataString))
		) {
			self::logThis('Incorrect argument passed: ' . $dataString, 3, 'vc_data_driver::decompress');
			return NULL;
		}
		
		//	Patch for VC 0.10 data format
		$tmp		=	explode('/', $dataString);
		$rawViews	=	array_shift($tmp);
		$rawViews	=	implode($tmp);				//	If there was more than 1 slash, just ignoring others
		
		//	Validate raw views
		if(	!isset($rawViews)	||
			("" == trim($rawViews))
		){
			self::logThis('Views number was not detected. Using 1 instead', 2, 'vc_data_driver::decompress');
			$rawViews	=	1;
		}
		$views	=	hexdec($rawViews);
		
		//	Validate raw IPs
		//	Split the string into 8-symbol-length pieces
		$rawIPs	=	str_split($rawVisitors, 8);
		$ips	=	array();
		foreach($rawIPs as $rawIP) {
			if(strlen($rawIP) != 8) {
				self::logThis('Corrupted data (raw visitor IP) met: ' . $rawIP . '. Ignoring.', 2, 'vc_data_driver::decompress');
				continue;
			}
			
			//	Split raw IP into sections and decompile it
			$ipSections	=	str_split($rawIP, 2);
			//array_walk($ipSections, 'hexdec');
			foreach($ipSections as &$sect) {
				$sect	=	hexdec($sect);
			}
			$ips[]		=	implode('.', $ipSections);
		}
		
		if(count($ips) < 1) {
			self::logThis('No valid visitors IPs detected. Probably data was corrupted. Terminating', 3, 'vc_data_driver::decompress');
			return NULL;
		}
		
		return array(
			'views' 	=>	$views,
			'visitors'	=>	$ips
		);
	}
	
	/**
	 * Compresses statistics for a batch of entries.
	 * Result array contains compressed lines of statistics.
	 * 
	 * @param	array	$data	Equal to $statistics variable from vc_core class
	 * 
	 * @return	array
	 */
	public function batchCompress(Array $data) {
		//	Validate parameter
		if(count($data) < 1) {
			self::logThis('Empty array passed. Terminating', 2, 'vc_data_driver::batchCompress');
			return array();
		}
		
		//	Apply compress function to each entry and return successful results
		$retval	=	array();
		foreach($data as $key => $entry) {
			self::logThis('Processing item with ID ' . $key, 0, 'vc_data_driver::batchCompress');
			
			if(	!isset($entry['views'])			||
				("" == trim($entry['views']))	||
				!isset($entry['visitors'])		||
				!is_array($entry['visitors'])	||
				(count($entry['visitors']) < 1)
			) {
				self::logThis('Wrongly composed data detected. Stacktrace:\n' . print_r($entry, true));
				continue;
			}
			
			$tmp			=	self::compress($entry['views'], $entry['visitors']);
			$retval[$key]	=	$tmp;
		}
		
		return $retval;
	}
	
	/**
	 * Decompresses a batch of statitics.
	 * Result array is in fact the same as $statistics variable from vc_core class
	 * 
	 * @param	array	$data
	 * 
	 * @return	array
	 */
	public function batchDecompress(Array $data) {
		//	Validate parameter
		if(count($data) < 1) {
			self::logThis('Empty array passed. Terminating', 2, 'vc_data_driver::batchDecompress');
			return array();
		}
		
		//	Applying function decompress to all entries
		$retval	=	array();
		foreach($data as $key => $entry) {
			self::logThis('Processing item with ID ' . $key, 0, 'vc_data_driver::batchDecompress');
			
			if(	!isset($entry)			||
				("" == trim($entry))
			) {
				self::logThis('Empty item detected. Ignoring', 2, 'vc_data_driver::batchDecompress');
				continue;
			}
			
			$tmp	=	self::decompress($entry);
			if(isset($tmp)) {
				$retval[$key]	=	$tmp;
			}
			
		}
		return $retval;
	}
	
	/**
	 * Returns the version of the driver
	 * 
	 * @return	string
	 */
	public function getVersion() {
		return self::VERSION;
	}
	
	/**
	 * Logs a message using a specific log driver
	 *
	 * @param string	$message	Message to log
	 * @param integer	$severity	Message severity
	 * @param string	$place		Where event has occured
	 */
	private function logThis($message, $severity, $place) {
		if(isset($this->logger)) {
			$this->logger->log($message, $severity, $place);
		}
	}
}
?>